#include "NGramas.h"
#include "cargadorDeArchivos.h"
#define CUALQUIER_PALABRA "zxcvb"
#define FACTOR_TRIGRAMA 0.7
#define FACTOR_BIGRAMA 0.25
#define NOMBRE_ARCHIVO_TRIGRAMAS "trigramas.txt"


map<string,sFreqArchivo> diccionario;

map<string, datosDeUnaPAlabraDelDiccionario> diccionarioCompleto;


float totalAparicionesAcumuladas(){
	float total = 0;
	for(auto& x: diccionario)
		total += (float) x.second.frequencia;
	return total;

	return 0;
}




void agregarTrigrama(string palabra1, string palabra2, string palabra3){
	agregarPalabraAlDiccionario(palabra1);
	agregarPalabraAlDiccionario(palabra2);
	agregarPalabraAlDiccionario(palabra3);

	diccionarioCompleto[palabra1].frequencia++;


	palabrasDeTrigrama palabras;
	palabras.segundaPalabra = diccionarioCompleto[palabra2].indice;
	palabras.terceraPalabra = diccionarioCompleto[palabra3].indice;

	if (diccionarioCompleto[palabra1].segundaYTerceraPalabra.find(palabras) == diccionarioCompleto[palabra1].segundaYTerceraPalabra.end()){

		//Se agrega el trigrama con freq = 0;
		diccionarioCompleto[palabra1].segundaYTerceraPalabra[palabras] = 0;
	}

	diccionarioCompleto[palabra1].segundaYTerceraPalabra[palabras]++;
}

void agregarPalabraAlDiccionario(string palabra){

	if ( diccionarioCompleto.find(palabra) == diccionarioCompleto.end() ){
		//Entrar aca significa que la palabra no esta en el diccionario
		diccionarioCompleto[palabra].frequencia = 0;
		diccionarioCompleto[palabra].indice = diccionarioCompleto.size();
		diccionarioCompleto[palabra].apariciones = new set<int>; //hacerle delete
		//diccionarioCompleto[palabra].segundaYTerceraPalabra = new map<palabrasDeTrigrama,int,comparadorDeTrigramas>;
	}
}

void agregarPalabraSueltaAlDiccionario(string palabra){
	agregarPalabraAlDiccionario(palabra);
	diccionarioCompleto[palabra].frequencia++;
}







void guardarDiccionario(){
	ofstream archivoDiccionario;
	archivoDiccionario.open("diccionario.txt");

	for( map<string,datosDeUnaPAlabraDelDiccionario>::iterator iterador = diccionarioCompleto.begin() ; iterador != diccionarioCompleto.end(); ++iterador){
		archivoDiccionario<<(*iterador).second.indice<<" ";
		archivoDiccionario<<(*iterador).first<<" ";
		archivoDiccionario<<(*iterador).second.frequencia<<"\n";
	}	//archivoDiccionario<<(*iterador).second.archivo<<"\n";
}


//Guarda los trigramas y vacia los sets y mapas
void guardarTrigramasAparicionesYVaciarDatos(){

	//por cada palabra
		//guardar todas las apariciones el set de apariciones se debe cargar afuera
		//guardar todos los trigramas
		//probar si es mas rapido hacer todo de una o hacer una pasada para guardar en el de apariciones primero y en el de trigramas despues
		//hacer clear de las listas+

	string fileNameApariciones;
	fileNameApariciones = "apariciones";

	int numeroDeArchivo = 0;
	std::ostringstream nro;
	nro << numeroDeArchivo;
	fileNameApariciones += nro.str();

	fileNameApariciones += ".txt";


	ofstream archivoDeApariciones;
	archivoDeApariciones.open(fileNameApariciones.c_str());//, ios::app);


	string fileNameTrigramas;
	fileNameTrigramas = "trigramas";
	fileNameTrigramas += nro.str();//Definido arriba
	fileNameTrigramas += ".txt";


	ofstream archivoDeTrigramas;
	archivoDeTrigramas.open(fileNameTrigramas.c_str());//, ios::app);

	for (map<string,datosDeUnaPAlabraDelDiccionario>::iterator itDicc = diccionarioCompleto.begin() ; itDicc != diccionarioCompleto.end(); ++itDicc){

		string palabraActual = (*itDicc).first;

		////////ITERACION SOBRE EL SET DE APARICIONES///////////////
		set<int>* setActual  = diccionarioCompleto[palabraActual].apariciones ;
		if ( setActual != nullptr){
			if (setActual->size() > 3){
				archivoDeApariciones<<diccionarioCompleto[palabraActual].indice<<" ";
				for (set<int>::iterator itSet =  setActual->begin(); itSet != setActual->end(); ++itSet){
					archivoDeApariciones<<(*itSet)<<" ";
				}
				archivoDeApariciones<<"$ \n";
				setActual->clear();
			}
		}

		////////ITERACION SOBRE EL SET DE TRIGRAMAS///////////////
		map<palabrasDeTrigrama,int,comparadorDeTrigramas>* mapActual = &diccionarioCompleto[palabraActual].segundaYTerceraPalabra;
		if (mapActual->size() > 0 ){
			for (map<palabrasDeTrigrama,int,comparadorDeTrigramas>::iterator itMap =  mapActual->begin(); itMap != mapActual->end(); ++itMap){
				archivoDeTrigramas<<diccionarioCompleto[palabraActual].indice<<" ";
				archivoDeTrigramas<<(*itMap).first.segundaPalabra<<" ";
				archivoDeTrigramas<<(*itMap).first.terceraPalabra<<" ";
				archivoDeTrigramas<<(*itMap).second<<"\n";
			}
		}
		mapActual->clear();
	}

	archivoDeApariciones.close();
	archivoDeTrigramas.close();

}






/*REEMPLAZO ESTO POR EL QUE TIENE EL DICCIONARIO COMPLETO
void agregarTrigrama(string palabra1, string palabra2, string palabra3){

	//SI NO ESTA, LA AGREGA CON FRECUENCIA 0 Y archivo = nullptr
	agregarPalabraAlDiccionario(palabra1);
	agregarPalabraAlDiccionario(palabra2);
	agregarPalabraAlDiccionario(palabra3);

	diccionario[palabra1].frequencia++;

	if (diccionario[palabra1].archivo == nullptr){
		string fileName;
		fileName = "trigramas/trigramas_empezados_con_";
		fileName += palabra1;
		fileName += ".txt"; // o .dat

		ofstream* archivoDeTrigramas = new  ofstream;
		archivoDeTrigramas->open(fileName.c_str(),ios::app);

		diccionario[palabra1].archivo = archivoDeTrigramas;
	}

	(*diccionario[palabra1].archivo)<<diccionario[palabra2].indice<<" "<<diccionario[palabra3].indice<<"\n";
}
*/
/* REEMPLAZO ESTO POR EL NUEVO DICCIONARIO
void agregarPalabraAlDiccionario(string palabra){
	if(palabra == "Tevez"){printf("Tevez entro en agregarpalabraAlDiccionario\n");}

	if ( diccionario.find(palabra) == diccionario.end() ){
		if(palabra == "Tevez"){printf("Se agrego Tevez al dicionario\n");}
		//Entrar aca significa que la palabra no esta en el diccionario
		diccionario[palabra].frequencia = 0;
		diccionario[palabra].archivo = nullptr;
		diccionario[palabra].indice = diccionario.size();

		if(palabra == "Tevez"){printf("Con el indice: %i\n", diccionario[palabra].indice);}
	}
}
*/

/* REEMPLAZO ESTO POR EL NUEVO DICCIONARIO
void guardarDiccionario(){
	ofstream archivoDiccionario;
	archivoDiccionario.open("diccionario.txt");

	for( map<string,sFreqArchivo>::iterator iterador = diccionario.begin() ; iterador != diccionario.end(); ++iterador){
		archivoDiccionario<<(*iterador).second.indice<<" ";
		archivoDiccionario<<(*iterador).first<<" ";
		archivoDiccionario<<(*iterador).second.frequencia<<"\n";
		//archivoDiccionario<<(*iterador).second.archivo<<"\n";
	}

	archivoDiccionario.close();
}*/





//REEMPLAZO ESTO POR EL NUEVO DICCIONARIO
/*void agregarPalabraSueltaAlDiccionario(string palabra){
	agregarPalabraAlDiccionario(palabra);
	diccionario[palabra].frequencia++;
}*/

void agregarTrigramas(vector<string> &palabras){

	agregarPalabraAlDiccionario(CUALQUIER_PALABRA);

	size_t tam = palabras.size();

	string ultimaPalabra1,ultimaPalabra2;


	if (tam > 2 ){
		for(size_t i = 0; i < tam - 2; i++){
			agregarTrigrama(palabras.at(i), palabras.at(i+1), palabras.at(i+2));
			ultimaPalabra1 = palabras.at(i+1);
			ultimaPalabra2 = palabras.at(i+2);
		}
		//agregarPalabraSueltaAlDiccionario(ultimaPalabra1);
		agregarTrigrama(ultimaPalabra1,ultimaPalabra2, CUALQUIER_PALABRA);
		agregarPalabraSueltaAlDiccionario(ultimaPalabra2);
	}
	else if (tam == 2 ){//Si una frase tuviera dos palabras, no funcionaria el for
		agregarTrigrama(palabras.at(0), palabras.at(1),CUALQUIER_PALABRA);
		agregarPalabraSueltaAlDiccionario(palabras.at(1));
	}
	else{
		agregarPalabraSueltaAlDiccionario(palabras.at(0));
	}

}



void cerrarArchivos(){
	for( map<string,sFreqArchivo>::iterator iterador = diccionario.begin() ; iterador != diccionario.end(); ++iterador){
		if ((*iterador).second.archivo != nullptr ){
			(*iterador).second.archivo->close();
			delete (*iterador).second.archivo;
		}
	}
}

void ordenarYReducirArchivoDeTrigramas(string palabraInicial){
	string fileName;
	fileName = "trigramas/trigramas_empezados_con_";
	fileName += palabraInicial;
	fileName += ".txt";
	ifstream archivoDeTrigramas;
	archivoDeTrigramas.open(fileName.c_str());

	map<palabrasDeTrigrama,int,comparadorDeTrigramas> mapaDeTrigramas;


	if(archivoDeTrigramas.is_open()){
		while(!archivoDeTrigramas.eof()){

			palabrasDeTrigrama trigrama;

		 	archivoDeTrigramas >> trigrama.segundaPalabra;

			if(!archivoDeTrigramas.eof()){

				archivoDeTrigramas >> trigrama.terceraPalabra;

				if (mapaDeTrigramas.find(trigrama) == mapaDeTrigramas.end()){
					mapaDeTrigramas[trigrama] = 0;
				}

				mapaDeTrigramas[trigrama]++;
			}
		}

		archivoDeTrigramas.close();

		guardarArchivoDeTrigramasReducido(&mapaDeTrigramas, palabraInicial);
	}
}


void guardarArchivoDeTrigramasReducido(map<palabrasDeTrigrama,int,comparadorDeTrigramas>* mapaDeTrigramas, string palabraInicial){

	ofstream archivoDeTrigrama;

	string fileName;
	fileName = "trigramas/trigramas_empezados_con_";
	fileName += palabraInicial;
	fileName += ".txt";
	ofstream archivoDeTrigramasReducido;
	archivoDeTrigramasReducido.open(fileName.c_str());

	for( map<palabrasDeTrigrama,int,comparadorDeTrigramas>::iterator iterador = mapaDeTrigramas->begin() ; iterador != mapaDeTrigramas->end(); ++iterador){
		archivoDeTrigramasReducido<<(*iterador).first.segundaPalabra<<" ";
		archivoDeTrigramasReducido<<(*iterador).first.terceraPalabra<<" ";
		archivoDeTrigramasReducido<<(*iterador).second<<"\n";
	}

	archivoDeTrigramasReducido.close();
}
//----------------------------------------------------------------------------------------

float probabilidadBigrama(string palabra1,
		string palabra2,
		diccionario_t* dic,
		diccionarioPorIndice_t* dicPorInd,
		mapaDeTrigramas_t* trigramas){

	termino_t termino1, termino2;
	bool ap1, ap2;
	ap1 = dic->mapa.find(palabra1) != dic->mapa.end();//palabraApareceEnDiccionario(palabra1, dicPorInd);
	ap2 = dic->mapa.find(palabra2) != dic->mapa.end();//palabraApareceEnDiccionario(palabra2, dicPorInd);
	if(ap1){
		termino1 = dic->mapa.at(palabra1);
	}
	if(ap2){
		termino2 = dic->mapa.at(palabra2);
	}

	if(!ap1){	// !ctx
		if(!ap2){
			return 0;
		}
		return FACTOR_BIGRAMA * ((float) termino2.frecuencia) / ((float)dic->total);
	}
	if(!ap2) return 0;

	float totalPalabras = (float) dic->total;
	float frecuenciaPrimeraPalabra = (float) termino1.frecuencia;
	float frecuenciaSegundaPalabra = (float) termino2.frecuencia;

	float frecuenciaBigrama = 0;

	if(trigramas->find(termino1.index) != trigramas->end()){
		if(trigramas->at(termino1.index).find(termino2.index) != trigramas->at(termino1.index).end()){
			map<int, int>* mapa = &(trigramas->at(termino1.index).at(termino2.index));
			for(auto& x : *mapa)
				frecuenciaBigrama += x.second;

			return ((1 - FACTOR_BIGRAMA) * frecuenciaBigrama / frecuenciaPrimeraPalabra)
					+ (FACTOR_BIGRAMA * frecuenciaSegundaPalabra / totalPalabras);
		}
		else{
			return 0;
		}
	}
	else{
		return 0;
	}
}

float probabilidadTrigrama(string palabra1,
		string palabra2,
		string palabra3,
		diccionario_t* dic,
		diccionarioPorIndice_t* dicPorInd,
		mapaDeTrigramas_t* trigramas){

	if(palabra3 == CUALQUIER_PALABRA)
		return probabilidadBigrama(palabra1, palabra2,
				dic, dicPorInd, trigramas);

	termino_t termino1, termino2, termino3;
	bool ap1, ap2, ap3;
	ap1 = dic->mapa.find(palabra1) != dic->mapa.end();//palabraApareceEnDiccionario(palabra1, dicPorInd);
	ap2 = dic->mapa.find(palabra2) != dic->mapa.end();//palabraApareceEnDiccionario(palabra2, dicPorInd);
	ap3 = dic->mapa.find(palabra3) != dic->mapa.end();//palabraApareceEnDiccionario(palabra3, dicPorInd);

	if(ap1){
		termino1 = dic->mapa.at(palabra1);
	}
	if(ap2){
		termino2 = dic->mapa.at(palabra2);
	}
	if(ap3){
		termino3 = dic->mapa.at(palabra3);
	}else{
		return 0;
	}

	if(!ap1 || !ap2) //el contexto no aparece
	    return ((float) termino3.frecuencia) / ((float) dic->total);

	float totalPalabras = dic->total;
	float frecuenciaPalabra3 = termino3.frecuencia;
	float frecuenciaPrimerBigrama, frecuenciaTrigramaCompleto;
	float frecuenciaSegundaPalabra = termino2.frecuencia;
	float frecuenciaSegundoBigrama;

	int ind1 = termino1.index;
	int ind2 = termino2.index;
	int ind3 = termino3.index;


	if(trigramas->find(ind1) != trigramas->end()){
		if(trigramas->at(ind1).find(ind2) != trigramas->at(ind1).end()){
			if(trigramas->at(ind1).at(ind2).find(ind3) != trigramas->at(ind1).at(ind2).end()){
				frecuenciaTrigramaCompleto = (float) trigramas->at(ind1).at(ind2).at(ind3);
			}else{
				return 0;
			}
		}else{
			return 0;
		}
	}else{
		return 0;
	}

	//Si llega aca es porque existe ind1-ind2-ind3, falta checkear ind2-ind3-###
	frecuenciaPrimerBigrama = 0;
	map<int, int>* mapa = &(trigramas->at(ind1).at(ind2));
	for(auto& x : *mapa){
		frecuenciaPrimerBigrama += x.second;
	}

	frecuenciaSegundoBigrama = 0;
	//Falta checkearlo, ahora lo checkeo
	if (trigramas->find(ind2) != trigramas->end()){
		if(trigramas->at(ind2).find(ind3) != trigramas->at(ind2).end()){
			mapa = &(trigramas->at(ind2).at(ind3));
			for(auto& y : *mapa){
				frecuenciaSegundoBigrama += y.second;
			}
		}
	}
	return (1 - FACTOR_BIGRAMA - FACTOR_TRIGRAMA) * frecuenciaPalabra3/totalPalabras
			+ FACTOR_BIGRAMA * frecuenciaSegundoBigrama/frecuenciaSegundaPalabra
			+ FACTOR_TRIGRAMA * frecuenciaTrigramaCompleto/frecuenciaPrimerBigrama;
}

void completarVectorDefault(vector<palabraYProbabilidad>* vec){
	palabraYProbabilidad terminoThe, terminoA, terminoComa, terminoComilla, terminoOf, terminoAnd;
	terminoThe.palabra = "the";
	terminoThe.probabilidad = 0.2;
	terminoA.palabra = "a";
	terminoA.probabilidad = 0.2;
	terminoComa.palabra = ",";
	terminoComa.probabilidad = 0.2;
	terminoComilla.palabra = "";
	terminoComilla.palabra += '"';
	terminoComilla.palabra += '"';
	terminoComilla.probabilidad = 0.2;
	terminoOf.palabra = "of";
	terminoOf.probabilidad = 0.2;
	terminoAnd.palabra = "and";
	terminoAnd.probabilidad = 0.2;
	vec->push_back(terminoThe);
	vec->push_back(terminoA);
	vec->push_back(terminoComa);
	vec->push_back(terminoComilla);
	vec->push_back(terminoOf);
	vec->push_back(terminoAnd);
}


void obtenerCandidatas(string palabra1, string palabra2,
                diccionario_t* dic, diccionarioPorIndice_t* dicPorIndice,
                mapaDeTrigramas_t* trigramas,
                vector<palabraYProbabilidad>* vec){
	int ind1, ind2;

	if(dic->mapa.find(palabra1) != dic->mapa.end()){
		ind1 = dic->mapa.at(palabra1).index;
	}else{
		completarVectorDefault(vec);
		return;
	}
	if(dic->mapa.find(palabra2) != dic->mapa.end()){
		ind2 = dic->mapa.at(palabra2).index;
	}else{
		completarVectorDefault(vec);
		return;
	}

	//ACA NO SE HACE UN CHECKEO, PUEDE SER QUE FALLE PORQUE SE FILTRA POR TRIGRAMAS DE FREQ > 3
	///Si llega aca es porque se encontraron los indices
	if (trigramas->find(ind1) != trigramas->end()){
		if (trigramas->at(ind1).find(ind2) != trigramas->at(ind1).end()){
			for(auto& x : trigramas->at(ind1).at(ind2)){
				if (dicPorIndice->find(x.first) != dicPorIndice->end()){
					palabraYProbabilidad pypActual;
					pypActual.palabra = dicPorIndice->at(x.first);
					pypActual.probabilidad = probabilidadTrigrama(
							palabra1, palabra2, pypActual.palabra,
							dic, dicPorIndice, trigramas);
					vec->push_back(pypActual);
				}
			}
		}else{
			completarVectorDefault(vec);
			return;
		}
	}else{
		completarVectorDefault(vec);
		return;
	}

	if(vec->size() == 0){
		completarVectorDefault(vec);
	}

//////////HARDCODEADO PARA VER QUE FUNCIONE//////////
/*	palabraYProbabilidad pypActual1;
	pypActual1.palabra = "for";
	pypActual1.probabilidad = 0.5;
	vec->push_back(pypActual1);

	palabraYProbabilidad pypActual2;
	pypActual2.palabra = "power";
	pypActual2.probabilidad = 0.2;
	vec->push_back(pypActual2);
*/
}

size_t encontrarHueco(vector<string>* palabras, diccionario_t* dic, diccionarioPorIndice_t* dicPorIndice, mapaDeTrigramas_t* trigramas){
	size_t cantidad = palabras->size();
	size_t indexMin = 1;
	float probabilidadMin = probabilidadTrigrama(palabras->at(0), palabras->at(1), CUALQUIER_PALABRA, dic, dicPorIndice, trigramas);
	float probabilidadActual;

	for(size_t index = 2; index < cantidad; index++){
		probabilidadActual = probabilidadTrigrama(palabras->at(index - 2), palabras->at(index - 1), palabras->at(index), dic, dicPorIndice, trigramas);
		if(probabilidadActual < probabilidadMin){
			probabilidadMin = probabilidadActual;
			indexMin = index;
		}
	}

	return indexMin;
}


